SimpleMIDIFile

part of wslib

a MIDI file implementation capable of reading and writing all types of

MIDI data (except sysex messages).

first an example:

(

m = SimpleMIDIFile( "~/Desktop/midifiletest.mid" ); // create empty file

m.init1( 3, 120, "4/4" ); // init for type 1 (multitrack); 3 tracks, 120bpm, 4/4 measures

m.timeMode = \seconds; // change from default to something useful

((0,(1/8)..5)).do({ |starttime| // add random notes

m.addNote( 36 + 50.rand, 32 + 96.rand, starttime, [0.1,0.05].choose, 127,

track: 1 )

});

((0,(1/4)..5)).do({ |starttime| // add random notes to next track

m.addNote( 36 + 50.rand, 64 + 64.rand, starttime, [0.1,0.025].choose, 127,

channel: 1, // note: = midi channel 2

track: 2 )

});

)

m.midiEvents.dopostln; // all midi events

m.metaEvents.dopostln; // notice the incorrect 'endOfTrack' events for track 1 & 2;

m.adjustEndOfTrack;

m.metaEvents.dopostln; // try again

m.tempoMap; // get the tempo map ( [[ startTime, bpm ], [ etc.. ]] )

m.timeSignatures; // get the time signatures ( ( [[ startTime, a/b ], [ etc.. ]] )

m.p.play; // convert to Pattern and play

m.write; // now play the file in Quicktime, or open with another app

m.plot; // uses ScaledUserView

SimpleMIDIFile also supports Event Patterns (see Pattern and Pbind helpfiles). You can use the p method to convert a SimpleMIDIFile into a playable pattern (a Ppar to be exact).

// download a Bach midi file:

"curl http://www.bachcentral.com/BachCentral/ORGAN/toccata1.mid -o ~/Desktop/toccata1.mid".unixCmd;

// read it

m = SimpleMIDIFile.read( "~/Desktop/toccata1.mid" );

// play it (cmd-. to stop)

m.p.play; // takes a few seconds to start because this midifile starts with a rest

// play it with another instrument

(

SynthDef( "organ", { |freq = 440, sustain = 1, amp = 0.1|

var sig;

sig = LFPar.ar( freq * [1,2,3,5], 0, amp/[2,4,5,7] );

Out.ar( 0, Env([0,1,1,0], [0.025,sustain,0.025]).kr(2) * sig.dup )

}).add;

);

x = m.p( \organ ).play;

x.stop;

Via the fromPattern method patterns can be turned into midi files as well:

(

p = Pbind(

\dur, Prand( [0.25,0.5,1], inf ),

\freq, Pwhite( 440, 880 ),

\db, Pwhite( -40,-10 ),

\legato, Pwhite( 0.25, 4 ) );

m = SimpleMIDIFile( "~/Desktop/testPat.mid" );

m.init1( 2, 120, "4/4" );

m.fromPattern( p );

)

m.plot;

m.p.play; // note numbers are not rounded

p.play; // compare

m.write; // when writing to file note numbers are rounded (MIDI file format doesn't allow floats)

Instance variables:

all variables stated here have getters and setters. However in some cases the setters

are actually separate methods which may change or convert other variables as well.

pathName

file path of the midi file. defaults to "~/scwork/midi.mid"

midiEvents

an array containing all MIDI events as arrays. Format:

[ trackNumber, absTime, type, channel, val1, val2 ]

trackNumber

the number of the track in which this event sits. For format type 0 files this is always 0,

for type 1 files track 0 is reserved for meta events, so midiEvent tracks start counting at 1

absTime

the absolute start-time of the event. If timeMode == \ticks the absTime is in ticks (tempo dependant),

if timeMode == \seconds it is in seconds (not depending on tempo). In the original midi file format all

times are relative to the previous event on a track, but for convenience SimpleMIDIFile converts them

to absolute times when reading in.

type

usually a Symbol which can be:

\noteOff

\noteOn

\polyTouch

\cc

\program

\afterTouch

\pitchBend

channel

midi channel on which the event plays. Starts at 0 (= midi channel 1)

val1, val2

values for the event. These are usually 0-127. The contents of these vary per event type:

type val1 val2

noteOn note number velocity

noteOff note number velocity

polyTouch note number amount

cc control number amount

program program number (no val2)

afterTouch amount (no val2)

pitchBend depends on pitchBendMode

metaEvents

an array containing all META events as arrays. Format:

[ trackNumber, absTime, type, [values] / value / string ]

The absTime represents the abslute time at which the event takes place. In the Standard MIDI file

format time values are always relative to the previous event. For convenience reasons the

SimpleMIDIFile class converts these to absolute times when reading a file.

The format of last item in the array above depends on the type of the event:

types of which the last item is a string

\text

\copyright

\trackName

\instName

\lyrics

\marker

\cuePoint

types of which the last item is a value

\sequenceNumber

\tempo -> tempo in beats per minute

types of which the last item is an array of values

\timeSignature

\keySignature

\sequencerSpecific

\midiChannelPrefix

types of which the last item something else

\smpteOffset -> a SMPTE

\endOfTrack -> nil

there can be unknown types of meta events. They have status byte number instead of

a symbol in the 'type' slot of the array

tempo

a global tempo indicator in bpm. If there's one or more tempo events in the metaEvents,

tempo will always indicate the tempo of the first one found. The tempo mapping functionality

uses the tempo events instead of this value. Setting the tempo with tempo_ doesn't add or change

any tempo event ( a MIDI file can but doesn't need to contain a tempo event ). To get the tempo

again from the metaEvents if their there use getTempo.

timeMode

can be 'ticks' or 'seconds'. Defaults to 'ticks'. This affects all time information

contained in the midiEvents and metaEvents. Whenever the timeMode is switched

all time data will be converted.

ticks

ticks are the standard MIDI file format for time data. The duration of one tick

depends on the division and tempo at that specific point.

seconds

seconds are absolute seconds, not influenced by tempo or division

division

number of 'ticks' per quarter note. This should usually be 1024. Change it only when

you experience problems with specific sequencer software (there seem to be some which

use different divisions)

format

can be 0, 1 or 2 (single, multi or pattern oriented)

0

format 0 files typically have all data in the first track (0)

1

format 1 files typically have al global metaData ins the first track (0)

and all midi data in the following tracks (1..). A trackName metaEvent

in track 0 is usually interpreted as the song name

2

format 2 is not very common. Works the same as format 1, but is meant for

separate patterns in one file, contained in sequences (tracks)

Empty SimpleMIDIFiles need to be inited as format 0 or 1 first, using the init0 and init1 methods

tracks

number of tracks used. Can be fetched from midiEvents using the adjustTracks method

pitchBendMode

can be 'int8', 'int16' or 'float'. The default is 'int8'. When the pitchBendMode is changed all existing

values are converted.

int8

pitchbend stored as array of 2 int8 values (0-127), as stored in the midifile

int16

pitchbend stored as single int16 value (0-16383)

float

pitchbend stored as floating point values (-1.0 - 1.0)

Methods

creation

*new ( pathname )

initiation

init0 ( inTempo, timeSignature )

init1 ( inTracks, inTempo, timeSignature ) // minimum tracks: 2; first is metaData

read / write

*read ( pathname )

read

write ( newFileName )

pattern support

p ( inst, amp )

fromPattern ( pattern, maxEvents = 200, maxAmp = 0.2, startTime = 0 )

generatePatternSeqs // returns sequenceable [note, dur] data in an array

correction

adjustTracks // adjust number of tracks according to midi data

adjustEndOfTrack ( track, wait )

correctTempoEvents ( removeDuplicates, removeDoubles )

sortMIDIEvents // sort events on time and track

sortMetaEvents

getTempo

conversion / processing

convertNoteOns ( noteOffVelo )

convertNoteOffs

shiftTime ( deltaTime )

selection

midiTrackEvents ( trackNumber )

midiChannelEvents ( channel, track )

midiTrackTypeEvents ( track, type, channel )

midiTypeEvents ( args )

noteEvents ( channel, track )

noteOnEvents ( channel, track )

realNoteOnEvents ( channel, track )

noteOffEvents ( channel, track )

firstNoteOnTrack ( track )

pitchBendEvents ( channel, track )

afterTouchEvents ( channel, track )

controllerEvents ( cc, channel, track )

ccEvents ( cc, channel, track )

modulationEvents ( channel, track )

breathEvents ( channel, track )

volumeEvents ( channel, track )

panEvents ( channel, track )

expressionEvents ( channel, track )

damperEvents ( channel, track )

metaTrackEvents ( trackNumber )

timeSignatureEvents

keySignatureEvents

smpteOffsetEvents

tempoEvents

endOfTrack ( track )

converted selection to different array formats or Classes

noteSustainEvents( channel, track )

// noteSustainEvents returns events in this form:

// [track, absTime, \noteOn, channel, note, velo, dur, upVelo]

midiDeltaEvents ( track, type, channel )

midiDeltaCCEvents ( track, cc, channel )

timeSignatures

keySignatures

trackNames

instNames

midiTracks

metaTracks

asDicts ( )

asNoteDicts ( channel, track )

envFromType ( track, type, channel )

envFromCC ( track, cc, channel )

extraction of single values

trackName ( track )

instName ( track )

length

smpteOffset

timing methods (converted selection, extraction)

tempi

tempoMap

tempoEnv

beatAtTime ( time )

timeAtBeat ( beat )

tempoAtTime ( time )

timeSignatureAsArray

measureAtBeat ( beat, measureFormat )

measureAtTime ( time, measureFormat )

beatAtMeasure ( measure, measureFormat )

timeAtMeasure ( measure, measureFormat )

addition of events

addMIDIEvent ( event, sort )

addAllMIDIEvents ( events, sort )

addMIDIEventToTrack ( event, track, sort )

addAllMIDIEventsToTrack ( events, track, sort )

addMIDITypeEvent ( type, channel, args, absTime, track, sort )

addAllMIDITypeEvents ( type, channel, args, absTime, track, sort )

addNote ( noteNumber, velo, startTime, dur, upVelo, channel, track, sort )

addCC ( cc, val, startTime, channel, track )

addMetaEvent ( event, sort )

addTrack ( name )

addTimeSignature ( div, denom, time, sort, removeOld )

addTimeSignatureString ( string, time, sort, removeOld )

addTempo ( tempo, time, sort, removeOld )

setTrackName ( name, track )

setInstName ( name, track )

setTempo ( newTempo )

removal

removeMetaEvents ( type, time, track )

removeTimeSignature ( time, doIt )

removeTempo ( time, doIt )

analysis

analyzeTypes

analyzeMetaTypes

analyzeChannels ( type )

analyzeChannel ( channel, track )

analyzeUsedChannels ( track )

analyzeUsedTracks

analyzeUsedEvents

analyzeTracks ( type )

analyzeCC

analyzeCCTracks

usedChannels ( track )

usedTracks ( channel )

countMIDIEvents ( type, track, channel )

info